package com.tyf.bookreader.manager;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.tyf.bookreader.component.CancelOrderSender;
import com.tyf.bookreader.component.LoginUser;
import com.tyf.bookreader.constant.CommonConstants;
import com.tyf.bookreader.constant.RedisConstants;
import com.tyf.bookreader.domain.BrBookUser;
import com.tyf.bookreader.domain.BrOrder;
import com.tyf.bookreader.domain.BrOrderDetails;
import com.tyf.bookreader.domain.BrUser;
import com.tyf.bookreader.exception.BrException;
import com.tyf.bookreader.mapper.BrBookUserMapper;
import com.tyf.bookreader.mapper.BrOrderDetailsMapper;
import com.tyf.bookreader.mapper.BrOrderMapper;
import com.tyf.bookreader.mapper.BrUserMapper;
import com.tyf.bookreader.service.RedisService;
import com.tyf.bookreader.utils.UserUtils;
import com.tyf.bookreader.vo.OrderDetailsVo;
import com.tyf.bookreader.vo.OrderVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Description TODO
 * @Author shallow
 * @Date 2023/5/4 20:44
 */
@Slf4j
@Component
public class OrderManager {

    @Autowired
    private BrOrderMapper orderMapper;

    @Autowired
    private BrOrderDetailsMapper orderDetailsMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private BrUserMapper userMapper;

    @Autowired
    private BrBookUserMapper bookUserMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private CancelOrderSender cancelOrderSender;


    public void createOrder(OrderVo orderVo){
        //信息准备
        BrOrder order = new BrOrder();
        order.setId(IdWorker.getId());
        order.setOrderNum(UUID.randomUUID().toString().replaceAll("-", ""));
        order.setUserId(UserUtils.getLoginUser().getUser().getId());
        double orderPrice = 0;
        //求订单总价，并把订单信息转化类型
        for (OrderDetailsVo detail : orderVo.getOrderDetailsVoList()) {
            if (UserUtils.getLoginUser().getBookIdList().contains(detail.getBookId())){
                throw new BrException("此书已购买，请勿重复下单");
            }
            orderPrice+=detail.getBookPrice();
            BrOrderDetails detailsToDb = BeanUtil.toBean(detail, BrOrderDetails.class);
            detailsToDb.setOrderId(order.getId());
            orderDetailsMapper.insert(detailsToDb);
        }
        order.setOrderPrice(orderPrice);
        order.setStatus(0);
        orderMapper.insert(order);
        senderOrderToDelayQueue(order.getId());
        redisService.hSet(RedisConstants.ORDER_KEY,String.valueOf(order.getId()),order,RedisConstants.COMMON_CACHE_TIME);
    }

    public void payOrder(Long orderId){
        //查询订单信息
        BrOrder order = (BrOrder) redisService.hGet(RedisConstants.ORDER_KEY, String.valueOf(orderId));
        if (Objects.isNull(order)){
            order = orderMapper.selectById(orderId);
        }
        if (Objects.isNull(order)){
            throw new BrException("无订单信息");
        }
        List<BrOrderDetails> orderDetailsList = orderDetailsMapper.selectList(new LambdaQueryWrapper<BrOrderDetails>().eq(BrOrderDetails::getOrderId, orderId));
        if (order.getStatus() == 1){
            throw new BrException("订单已支付");
        }
        Long userId = UserUtils.getLoginUser().getUser().getId();
        //使用分布式锁防止多线程问题,一个用户同时只能支付一个订单
        RLock lock = redissonClient.getLock(RedisConstants.REDISSON_LOCK_PAY_ORDER + userId);
        try {
            if (lock.tryLock(RedisConstants.REDISSON_LOCK_WAIT,RedisConstants.REDISSON_LOCK_RELEASE, TimeUnit.SECONDS)) {
                //判断用户余额是否足够支付
                BrUser user = userMapper.selectById(userId);
                Double accountBalance = user.getAccountBalance();
                double balance = accountBalance - order.getOrderPrice();
                if (balance <0){
                    throw new BrException("用户余额不足");
                }
                //修改余额
                user.setAccountBalance(balance);
                userMapper.updateById(user);
                //修改订单信息
                order.setStatus(1);
                order.setPayTime(LocalDateTime.now());
                orderMapper.updateById(order);
                //记录已购图书
                //增加权限
                List<Long> tmp = UserUtils.getLoginUser().getBookIdList();
                for (BrOrderDetails orderDetails : orderDetailsList) {
                    bookUserMapper.insert(BrBookUser.builder()
                            .userId(userId)
                            .bookId(orderDetails.getBookId())
                            .build());
                    tmp.add(orderDetails.getBookId());
                }
                updateBookIdList(tmp);
            }else{
                throw new BrException("支付失败，其他订单支付中");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            redisService.hDel(RedisConstants.ORDER_KEY,String.valueOf(orderId));
            log.debug("bookList:{}",UserUtils.getLoginUser().getBookIdList());
        }
    }

    private  void updateBookIdList(List<Long> bookIdList){
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        loginUser.setBookIdList(bookIdList);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        redisService.set("login:token:" + loginUser.getUser().getId(), loginUser, RedisConstants.LOGIN_USER_TTL);
    }


    private void senderOrderToDelayQueue(Long orderId) {
        //发送延迟消息
        cancelOrderSender.sendMessage(orderId, CommonConstants.QUEUE_DEAD_TTL_TEST);
    }

    public void cancelOrder(Long orderId) {
        redisService.hDel(RedisConstants.ORDER_KEY,String.valueOf(orderId));
        BrOrder order = orderMapper.selectById(orderId);
        if (Objects.isNull(order)||order.getDeleted() == 1){
            throw new BrException("订单已不存在");
        }
        order.setStatus(3);
        orderMapper.updateById(order);
        redisService.hSet(RedisConstants.ORDER_KEY,String.valueOf(order.getId()),order,RedisConstants.COMMON_CACHE_TIME);
    }

    public void deleteOrder(Long orderId) {
        orderMapper.deleteById(orderId);
        redisService.hDel(RedisConstants.ORDER_KEY,String.valueOf(orderId));
    }
}
